﻿//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;

//namespace Jacaranda.Model
//{
//    public class InClause : ConditionalExpression
//    {
//        public InClause()
//        {
//            IsDemandingSource = true;
//        }

//        internal InClause Child { get; set; }

//        private Type demandedType;
//        internal Type DemandedType
//        {
//            get { return demandedType; }
//            set
//            {
//                demandedType = value;

//                if (Child != null)
//                {
//                    Child.DemandedType = IsDemandingSource ? Association.To : Association.From;
//                }
//            }
//        }

//        internal Type Type1 { get; set; }

//        internal Type Type2 { get; set; }

//        internal int Instance { get; set; }

//        internal string TopMostKeyFieldName { get; set; }

//        internal ConditionalExpression Condition1 { get; set; }

//        internal ConditionalExpression Condition2 { get; set; }

//        internal bool IsTopMost { get; set; }

//        internal bool IsDemandingSource { get; set; }

//        private InClause Innermost
//        {
//            get
//            {
//                InClause clause = this;
//                while (clause.Child != null)
//                {
//                    clause = clause.Child;
//                }

//                return clause;
//            }
//        }

//        public InClause In(Type type1, Type type2, ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            Innermost.Child = new InClause()
//            {
//                Type1 = type1,
//                Type2 = type2,
//                Condition1 = condition1,
//                Condition2 = condition2,
//                IsDemandingSource = true,
//            };

//            return this;
//        }

//        public InClause In<T1, T2>(ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            Innermost.Child = new InClause()
//            {
//                Type1 = typeof(T1),
//                Type2 = typeof(T2),
//                Condition1 = condition1,
//                Condition2 = condition2,
//                IsDemandingSource = true,
//            };

//            return this;
//        }

//        public InClause In<T1, T2>(ConditionalExpression condition1)
//        {
//            return In<T1, T2>(condition1, null);
//        }

//        public InClause In<T1, T2>()
//        {
//            return In<T1, T2>(null, null);
//        }

//        private InClause In<T1, T2>(string associationPropertyName, bool isDemandingSource, ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            Innermost.Child = new InClause()
//            {
//                Type1 = typeof(T1),
//                Type2 = typeof(T2),
//                Instance = Metadata.Type(typeof(T1)).AssociationFields.First(i => i.PropertyName == associationPropertyName).Association.Instance,
//                Condition1 = condition1,
//                Condition2 = condition2,
//                IsDemandingSource = isDemandingSource
//            };

//            return this;
//        }

//        public InClause In<T1, T2>(string associationPropertyName, ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            if (typeof(T1).Equals(typeof(T2)))
//            {
//                throw new InvalidOperationException("Jacaranda.Database.In: T1 and T2 cannot be the same. Call InSource or InTarget instead.");
//            }

//            return In<T1, T2>(associationPropertyName, true, condition1, condition2);
//        }

//        public InClause InSource<T1, T2>(string associationPropertyName, ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            if (!typeof(T1).Equals(typeof(T2)))
//            {
//                throw new InvalidOperationException("Jacaranda.Database.InSource: T1 and T2 must be the same. Call In instead.");
//            }

//            return In<T1, T2>(associationPropertyName, true, condition1, condition2);
//        }

//        public InClause InSource<T1, T2>(string associationPropertyName)
//        {
//            return InSource<T1, T2>(associationPropertyName, null, null);
//        }

//        public InClause InTarget<T1, T2>(string associationPropertyName, ConditionalExpression condition1, ConditionalExpression condition2)
//        {
//            if (!typeof(T1).Equals(typeof(T2)))
//            {
//                throw new InvalidOperationException("Jacaranda.Database.InTarget: T1 and T2 must be the same. Call In instead.");
//            }

//            return In<T1, T2>(associationPropertyName, false, condition1, condition2);
//        }

//        public InClause InTarget<T1, T2>(string associationPropertyName)
//        {
//            return InTarget<T1, T2>(associationPropertyName, null, null);
//        }


//        public InClause No<T1, T2>()
//        {
//            Innermost.Child = new NoClause()
//            {
//                Type1 = typeof(T1),
//                Type2 = typeof(T2),
//            };

//            return this;
//        }


//        private InClause First()
//        {
//            InClause first = this;

//            while (first.Child != null)
//            {
//                first = first.Child;
//            }
//            return first;
//        }


//        private string CreateConditionExpression(EntityMetadata m, string key, ConditionalExpression condition)
//        {
//            bool isPrimaryKeyMatchCondition = condition is FieldMatchCriteria &&
//                (condition as FieldMatchCriteria).Field is AbstractProperty &&
//                m.Fields.FirstOrDefault(i => i.PropertyName == ((condition as FieldMatchCriteria).Field as AbstractProperty).Name) != null &&
//                m.Fields.FirstOrDefault(i => i.PropertyName == ((condition as FieldMatchCriteria).Field as AbstractProperty).Name).IsPrimaryKey &&
//                (condition as FieldMatchCriteria).Operator == BinaryOperator.Equals;

//            if (isPrimaryKeyMatchCondition)
//            {
//                return string.Format("{0} = '{1}'", key, (condition as FieldMatchCriteria).Value);
//            }
//            else
//            {
//                return string.Format("{0} IN (SELECT {1} FROM {2} WHERE {3})",
//                                          key,
//                                          m.PrimaryKey.FieldName,
//                                          m.TableName,
//                                          condition.GenerateSql(m.Type));
//            }
//        }

//        private string CreateConditionalClause()
//        {
//            EntityMetadata entity1 = Metadata.Type(Type1);
//            EntityMetadata entity2 = Metadata.Type(Type2);

//            if (Condition1 == null && Condition2 == null)
//            {
//                return null;
//            }
//            else if (Condition1 != null && Condition2 == null)
//            {
//                StringBuilder b = new StringBuilder();

//                foreach (EntityMetadata m in entity1.RelatedEntities)
//                {
//                    b.Append(CreateConditionExpression(m, "Key1", Condition1));
//                    b.Append(" OR ");
//                }

//                return b.ToString().Substring(0, b.ToString().Length - 4);
//            }
//            else if (Condition1 == null && Condition2 != null)
//            {
//                StringBuilder b = new StringBuilder();

//                foreach (EntityMetadata m in entity2.RelatedEntities)
//                {
//                    b.Append(CreateConditionExpression(m, "Key2", Condition2));
//                    b.Append(" OR ");
//                }

//                return b.ToString().Substring(0, b.ToString().Length - 4);
//            }
//            else if (Condition1 != null && Condition2 != null)
//            {
//                StringBuilder b = new StringBuilder();

//                foreach (EntityMetadata m1 in entity1.RelatedEntities)
//                {
//                    foreach (EntityMetadata m2 in entity2.RelatedEntities)
//                    {
//                        b.Append(string.Format("{0} AND {1}", CreateConditionExpression(m1, "Key1", Condition1), CreateConditionExpression(m2, "Key2", Condition2)));
//                        b.Append(" OR ");
//                    }
//                }

//                return b.ToString().Substring(0, b.ToString().Length - 4);
//            }

//            throw new Exception();
//        }


//        private EntityAssociation association;
//        protected EntityAssociation Association
//        {
//            get
//            {
//                if (association == null)
//                {
//                    association = Metadata.Associations.FirstOrDefault(i => i.From.Equals(Type1) && i.To.Equals(Type2) && i.Instance == Instance);
//                    if (association == null)
//                    {
//                        association = Metadata.Associations.First(i => i.From.IsAssignableFrom(Type1) && i.To.IsAssignableFrom(Type2) && i.Instance == Instance);
//                    }
//                }

//                return association;
//            }
//        }

//        protected bool IsForwardDirection
//        {
//            get
//            {
//                if (Association.From.Equals(Association.To))
//                {
//                    return IsDemandingSource;
//                }
//                else
//                {
//                    return Association.From.Equals(DemandedType);
//                }
//            }
//        }


//        internal override string GenerateSql(Type propertyEntityType)
//        {
//            string keyToSelect = IsForwardDirection ? "Key1" : "Key2";
//            string keyToAssociate = IsForwardDirection ? "Key2" : "Key1";

//            string condition = CreateConditionalClause();

//            if (Child == null)
//            {
//                if (string.IsNullOrEmpty(condition))
//                {
//                    return string.Format("{0} IN (SELECT {1} FROM {2})",
//                        IsTopMost ? TopMostKeyFieldName : null,
//                        keyToSelect, 
//                        Association.TableName);
//                }
//                else
//                {
//                    return string.Format("{0} IN (SELECT {1} FROM {2} WHERE ({3}))",
//                        IsTopMost ? TopMostKeyFieldName : null,
//                        keyToSelect,
//                        Association.TableName,
//                        condition);
//                }
//            }
//            else
//            {
//                if (string.IsNullOrEmpty(condition))
//                {
//                    return string.Format("{0} IN (SELECT {1} FROM {2} WHERE {3} {4})",
//                        IsTopMost ? TopMostKeyFieldName : null,
//                        keyToSelect,
//                        Association.TableName,
//                        keyToAssociate,
//                        Child.GenerateSql(null));
//                }
//                else
//                {
//                    return string.Format("{0} IN (SELECT {1} FROM {2} WHERE ({3}) AND {4} {5})",
//                    IsTopMost ? TopMostKeyFieldName : null,
//                    keyToSelect,
//                    Association.TableName,
//                    condition,
//                    keyToAssociate,
//                    Child.GenerateSql(null));
//                }
//            }
//        }
//    }
//}
